﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using JEng_Mark_1.GameComponents;
using JEng_Mark_1.Managers;
using JEng_Mark_1.Models;
using JEng_Mark_1.SceneObject;


namespace JEng_Mark_1
{
    public partial class JEng : Game
    {
        /// <summary>
        /// Width and height of visible render window
        /// </summary>
        protected static int _rwWidth, _rwHeight;

        /// <summary>
        /// Get the width of the render window
        /// </summary>
        public static int Width { get { return _rwWidth; } }

        /// <summary>
        /// Get the height of the render window
        /// </summary>
        public static int Height { get { return _rwHeight; } }

        /// <summary>
        /// Aspect ratio of the render area (For widescreen game support)
        /// </summary>
        private static float _rwAspectRatio = 1.0f;

        /// <summary>
        /// Get the aspect ratio of the render window
        /// </summary>
        public static float AspectRatio { get { return _rwAspectRatio; } }

        /// <summary>
        /// Default color of background
        /// </summary>
        private static Color _bgColor = Color.Black;

        /// <summary>
        /// Get / Set background color for scenes
        /// </summary>
        public static Color BackgroundColor { get { return _bgColor; } set { _bgColor = value; } }

        /// <summary>
        /// Platform the engine is running on
        /// </summary>
        public static PlatformID CurrentPlatform = Environment.OSVersion.Platform;

        /// <summary>
        /// Render window title
        /// </summary>
        private static string _rwTitle = "";

        /// <summary>
        /// Get the title of the of the render window
        /// </summary>
        public static string WindowTitle { get { return _rwTitle; } }

        private bool _isAppActive = false;

        /// <summary>
        /// Get / Set flag determining if the engine is active
        /// </summary>
        public bool IsAppActive { get { return _isAppActive; } set { _isAppActive = value; } }

        protected static GraphicsDeviceManager _gDeviceManager = null;

        /// <summary>
        /// Get the current graphics device
        /// </summary>
        public static GraphicsDevice Device
        {
            get { return _gDeviceManager.GraphicsDevice; }
        }

        protected static ContentManager _cManager;

        /// <summary>
        /// Get the content manager for loading assets
        /// </summary>
        public static ContentManager ContentManager { get { return _cManager; } }

        private static JFpsCounter _fpsCounter = null;

        /// <summary>
        /// Get the fps counter object
        /// </summary>
        public static JFpsCounter FpsCounter { get { return _fpsCounter; } }

        private static JShaderManager _shaderManager;

        /// <summary>
        /// Get the shader manager
        /// </summary>
        public static JShaderManager ShaderManager { get { return _shaderManager; } }

        private static JTextureManager _textureManager;

        /// <summary>
        /// Get the texture manager
        /// </summary>
        public static JTextureManager TextureManager { get { return _textureManager; } }

        private static JInput _input;

        /// <summary>
        /// Get the input manager
        /// </summary>
        public static JInput Input { get { return _input; } }

        private static JScreenManager _screenManager = null;

        /// <summary>
        /// Get the screen manager
        /// </summary>
        public static JScreenManager ScreenManager { get { return _screenManager; } }

        private static JSceneGraphManager _sceneGraphManager = null;

        /// <summary>
        /// Get the scene graph manager
        /// </summary>
        public static JSceneGraphManager SceneGraphManager { get { return _sceneGraphManager; } }

        private static JCameraManager _cameraManager = null;

        /// <summary>
        /// Get the camera manager
        /// </summary>
        public static JCameraManager CameraManager { get { return _cameraManager; } }

        protected static JModelManager _modelManager;

        /// <summary>
        /// Get the model manager
        /// </summary>
        public static JModelManager ModelManager { get { return _modelManager; } }

        protected static JLuaManager _luaManager;

        /// <summary>
        /// Get the lua scripting engine manager
        /// </summary>
        public static JLuaManager LuaManager { get { return _luaManager; } }

        protected static JFileVerifyManager _fileVerifyManager;

        /// <summary>
        /// Get the file tampering verification manager
        /// </summary>
        public static JFileVerifyManager FileVerificationManager { get { return _fileVerifyManager; } }

        private static bool _checkedGraphicsOptions = false;
        private static bool _applyDeviceChanges = false;

        private IntPtr _drawSurface;

        /// <summary>
        /// Construct the base of engine, used by all constructors
        /// </summary>
        private void BaseConstruct()
        {
            //load and set engine settings
            JLog.addMessage("Loading game settings");
            JGameSettings.Initialize();

            ApplyResolutionChange();

            //Highest framerates possible
            _gDeviceManager.SynchronizeWithVerticalRetrace = false;

            //Realtime engine
            this.IsFixedTimeStep = false;

            //initial content manager
            JLog.addMessage("Initializing ContentManager");
            _cManager = new ContentManager(this.Services);

            //init input systems
            JLog.addMessage("Initializing input systems");
            _input = new JInput(this);
            Components.Add(_input);

            //init frame counter
            JLog.addMessage("Initializing FPSCounter");
            _fpsCounter = new JFpsCounter(this);
            Components.Add(_fpsCounter);

            //init the camera manager
            JLog.addMessage("Initializing CameraManager");
            _cameraManager = new JCameraManager(this);
            Components.Add(_cameraManager);

            //init shader manager
            JLog.addMessage("Initializing ShaderManager");
            _shaderManager = new JShaderManager(this);
            Components.Add(_shaderManager);

            //init texture manager
            JLog.addMessage("Initializing TextureManager");
            _textureManager = new JTextureManager(this);
            Components.Add(_textureManager);

            //init the screen manager
            JLog.addMessage("Initializing ScreenManager");
            _screenManager = new JScreenManager(this);
            Components.Add(_screenManager);

            //init the scene graph manager
            JLog.addMessage("Initializing SceneGraphManager");
            _sceneGraphManager = new JSceneGraphManager(this);
            Components.Add(_sceneGraphManager);

            //init the model manager
            JLog.addMessage("Initializing ModelManager");
            _modelManager = new JModelManager(this);
            Components.Add(_modelManager);            

            //init the tampering verification manager
            JLog.addMessage("Initializing FileVerifier");
            _fileVerifyManager = new JFileVerifyManager();
            
            //finially (THIS HAS TO BE DONE LAST) init the LuaScript engine
            _luaManager = new JLuaManager();
            JLuaManager.Initialize();
        }

        /// <summary>
        /// Construct the game, use this constructor if the game will be run through a windows form
        /// </summary>
        /// <param name="drawSurface">Surface to draw engine output too</param>
        protected JEng(IntPtr drawSurface)
        {
            _drawSurface = drawSurface;

            JLog.addMessage("Initializing GraphicsDeviceManager");
            _gDeviceManager = new GraphicsDeviceManager(this);

            //set minimum graphic requirements
            JLog.addMessage("Setting mininum shader requirements");
            _gDeviceManager.MinimumPixelShaderProfile = ShaderProfile.PS_2_0;
            _gDeviceManager.MinimumVertexShaderProfile = ShaderProfile.VS_2_0;

            _gDeviceManager.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(_gDeviceManager_PreparingDeviceSettingsWinForm);

#if XBOX360
            JLog.addError("Cannot use the winform constructor on Xbox360!");
            Exit();
#else
            System.Windows.Forms.Control.FromHandle((this.Window.Handle)).VisibleChanged += new EventHandler(VisibleChanged);
#endif
            BaseConstruct();
        }

        /// <summary>
        /// Basic engine initializer
        /// </summary>
        /// <param name="windowTitle">Render window title</param>
        protected JEng(string windowTitle,string luaInitPath)
        {
            JLog.addMessage("Initializing GraphicsDeviceManager");
            _gDeviceManager = new GraphicsDeviceManager(this);

            //set minimum graphic requirements
            JLog.addMessage("Setting mininum shader requirements");
            _gDeviceManager.MinimumPixelShaderProfile = ShaderProfile.PS_2_0;
            _gDeviceManager.MinimumVertexShaderProfile = ShaderProfile.VS_2_0;

            _gDeviceManager.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(_gDeviceManager_PreparingDeviceSettings);

            //initialize the lua engine
            JLuaManager.Initialize();

            //run the initialization script
            JLuaManager.ScriptingEngine.DoFile(luaInitPath);

            //get the config path
            //JConfigurationManager.ConfigurationPath = (string)JLuaManager.ScriptingEngine["configPath"];

            //setup manager
            BaseConstruct();


        }

        /// <summary>
        /// Default constructor
        /// </summary>
        protected JEng() : this("Game","Content/Lua/game.lua") { }

        /// <summary>
        /// Prepare the graphics device
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">event args</param>
        void _gDeviceManager_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            if (Environment.OSVersion.Platform != PlatformID.Win32NT)
            {
                PresentationParameters presentParams = e.GraphicsDeviceInformation.PresentationParameters;
                if (_gDeviceManager.PreferredBackBufferHeight == 720)
                {
                    presentParams.MultiSampleType = MultiSampleType.FourSamples;
#if !DEBUG
                    presentParams.PresentationInterval = PresentInterval.One;
#endif
                }
                else
                {
                    presentParams.MultiSampleType = MultiSampleType.TwoSamples;
#if !DEBUG
                    presentParams.PresentationInterval = PresentInterval.Two;
#endif
                }
            }
        }

        /// <summary>
        /// Prepare the graphics device
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">event args</param>
        void _gDeviceManager_PreparingDeviceSettingsWinForm(object sender, PreparingDeviceSettingsEventArgs e)
        {
            //Reroute drawing to our form control
#if !XBOX360
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = _drawSurface;
#else
            JLog.addError("Cannot use WinForms with Xbox360!");
            Exit();
#endif

            if (Environment.OSVersion.Platform != PlatformID.Win32NT)
            {
                PresentationParameters presentParams = e.GraphicsDeviceInformation.PresentationParameters;
                if (_gDeviceManager.PreferredBackBufferHeight == 720)
                {
                    presentParams.MultiSampleType = MultiSampleType.FourSamples;
#if !DEBUG
                    presentParams.PresentationInterval = PresentInterval.One;
#endif
                }
                else
                {
                    presentParams.MultiSampleType = MultiSampleType.TwoSamples;
#if !DEBUG
                    presentParams.PresentationInterval = PresentInterval.Two;
#endif
                }
            }
        }

        private void VisibleChanged(object sender, EventArgs e)
        {
            if (System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible == true)
                System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible = false;
        }

        public static void CheckOptionsAndPSVersion()
        {
            if (_gDeviceManager.GraphicsDevice == null)
            {
                throw new InvalidOperationException("Graphics device has not yet been created!");
            }
            _checkedGraphicsOptions = true;
        }

        /// <summary>
        /// Initialize graphics settings from the game settings
        /// </summary>
        public static void ApplyResolutionChange()
        {
            //get the resolution to change to from the game settings
            int resW = JGameSettings.Default.ResolutionWidth;
            int resH = JGameSettings.Default.ResolutionHeight;

            if (resW <= 0 || resH <= 0)
            {
                resW = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
                resH = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
            }

#if XBOX360
            //Xbox 360 specific settings
            _gDeviceManager.IsFullScreen = true;
            _gDeviceManager.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
            _gDeviceManager.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
#else
            _gDeviceManager.PreferredBackBufferWidth = resW;
            _gDeviceManager.PreferredBackBufferHeight = resH;
            //set fullscreen if the game setting decry
            _gDeviceManager.IsFullScreen = JGameSettings.Default.Fullscreen;
            _applyDeviceChanges = true;
#endif
            //log the resolution change
            JLog.addMessage("Changing resolution to " + resW + "x" + resH);
        }

        /// <summary>
        /// Initialize any engine components before running
        /// </summary>
        protected override void Initialize()
        {
            //Add initialization here
            base.Initialize();

            _gDeviceManager.DeviceReset += new EventHandler(_gDeviceManager_DeviceReset);
            _gDeviceManager_DeviceReset(null, EventArgs.Empty);
        }

        void _gDeviceManager_DeviceReset(object sender, EventArgs e)
        {
            // Update width and height
            _rwWidth = _gDeviceManager.GraphicsDevice.Viewport.Width;
            _rwHeight = _gDeviceManager.GraphicsDevice.Viewport.Height;
            _rwAspectRatio = (float)_rwWidth / (float)_rwHeight;
            JCameraManager.SetAllCamerasProjectionMatrix(_rwAspectRatio);            
        }

        /// <summary>
        /// Load any content needed by the game engine before running
        /// </summary>
        protected override void LoadContent()
        {
            //Load any content needed for engine
            //base.LoadContent();
        }

        /// <summary>
        /// Unload content needed by the game engine at shutdown
        /// </summary>
        protected override void UnloadContent()
        {
            //save file tampering file
            JFileVerifyManager.Write(_fileVerifyManager, "Content/security.sec");
            //save the log
            JLog.Save();
        }

        /// <summary>
        /// Allows the engine to run logic each frame
        /// </summary>
        /// <param name="gameTime">Timing snapshot</param>
        protected override void Update(GameTime gameTime)
        {
            //
            base.Update(gameTime);
        }

        /// <summary>
        /// Draw the screen
        /// </summary>
        /// <param name="gameTime">Timing snapshot</param>
        protected override void Draw(GameTime gameTime)
        {
            Device.Clear(_bgColor);

            base.Draw(gameTime);

            //if we need apply device settings
            if (_applyDeviceChanges)
            {
                _gDeviceManager.ApplyChanges();
                _applyDeviceChanges = false;
            }
        }

        protected override void OnActivated(object sender, EventArgs args)
        {
            base.OnActivated(sender, args);
            IsAppActive = true;
        }

        protected override void OnDeactivated(object sender, EventArgs args)
        {
            base.OnDeactivated(sender, args);
            IsAppActive = false;
        }
    }
}
